package com.ziytek.web.citizen.service;

import com.ziytek.web.citizen.constant.NumConstant;
import com.ziytek.web.citizen.model.bean.DischargeCapacity;
import com.ziytek.web.citizen.model.bean.DischargeData;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import java.util.LinkedList;

/**
 * 自适应辛普森法优化版（动态精度控制）
 *
 * @author fenghx
 */

@Slf4j
@Service
public class AdaptiveSimpsonService {
    /**
     * 精度阈值
     */
    private static final double EPSILON = 1e-6;
    /**
     * 最大递归深度，防止无限递归
     */
    private static final int MAX_RECURSION_DEPTH = 50;


    public DischargeCapacity adaptiveCalculate(LinkedList<DischargeData> data) {
        var capacity = new DischargeCapacity();
        if (CollectionUtils.isEmpty(data)) {
            capacity.setTimestamp(NumConstant.LONG_ZERO);
            capacity.setCapacity(NumConstant.DOUBLE_ZERO);
            return capacity;
        }

        double mah = recursiveSimpson(data, NumConstant.INT_ZERO, data.size() - NumConstant.INT_ONE, EPSILON, NumConstant.INT_ZERO);
        double second = (data.getLast().getTimestamp().toEpochSecond(ZoneOffset.UTC) - data.getFirst().getTimestamp().toEpochSecond(ZoneOffset.UTC));

        capacity.setCapacity(mah);
        capacity.setTimestamp(Math.abs((((Double) second)).longValue()));
        return capacity;
    }

    private double recursiveSimpson(LinkedList<DischargeData> data, int startIdx, int endIdx, double eps, int depth) {
        if (depth > MAX_RECURSION_DEPTH) {
            // 强制终止递归，返回当前最佳估计值
            long second = ChronoUnit.SECONDS.between(data.get(endIdx).getTimestamp(), data.get(startIdx).getTimestamp());
            double h = Math.abs(second) / 3600.0;
            return simpsonFormula(data, startIdx, endIdx, h);
        }

        //log.info("测试数据：{}",data);

        int midIdx = (startIdx + endIdx) / NumConstant.INT_TWO;
        if (data.get(endIdx) == null || data.get(endIdx).getTimestamp() == null) {
            return NumConstant.DOUBLE_ZERO;
        }
        if (data.get(startIdx) == null || data.get(startIdx).getTimestamp() == null) {
            return NumConstant.DOUBLE_ZERO;
        }

        long second = ChronoUnit.SECONDS.between(data.get(endIdx).getTimestamp(), data.get(startIdx).getTimestamp());

        double h = Math.abs(second) / 3600.0;

        double s = simpsonFormula(data, startIdx, endIdx, h);
        double sLeft = simpsonFormula(data, startIdx, midIdx, h / NumConstant.INT_TWO);
        double sRight = simpsonFormula(data, midIdx, endIdx, h / NumConstant.INT_TWO);
        if (Math.abs(sLeft + sRight - s) <= NumConstant.INT_FIFTEEN * eps) {
            return sLeft + sRight + (sLeft + sRight - s) / NumConstant.INT_FIFTEEN;
        }

        return recursiveSimpson(data, startIdx, midIdx, eps / NumConstant.INT_TWO, depth + NumConstant.INT_ONE)
                + recursiveSimpson(data, midIdx, endIdx, eps / NumConstant.INT_TWO, depth + NumConstant.INT_ONE);
    }

    private double simpsonFormula(LinkedList<DischargeData> data, int a, int b, double h) {
        return h / 6.0 * (data.get(a).getCurrent()
                + 4 * data.get((a + b) / NumConstant.INT_TWO).getCurrent()
                + data.get(b).getCurrent());
    }
}